#include "ProcessPool.hpp"
#include <iostream>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

class Channel // 进程管道类
{
public:
    Channel(int wfd, pid_t pid, const std::string &name)
        : _wfd(wfd), _pid(pid), _name(name)
    {
    }
    const std::string &GetName()
    {
        return _name;
    }
    pid_t GetPid()
    {
        return _pid;
    }
    int GetWfd()
    {
        return _wfd;
    }

    // 关闭文件
    void CloseWfd()
    {
        close(_wfd);
    }

    // 等待子进程，回收
    void Wait()
    {
        int status;
        int ret = waitpid(_pid, &status, 0);
        if (ret > 0)
        {
            std::cout << " Wait sucess " << _pid << std::endl;
        }
        else
        {
            std::cout << " Wait no sucess " << " wait form " << (status & 0x7f)\ 
            << " quit code "
                      << (status >> 8 & 0x7f) << std::endl;
            exit(1);
        }
    }
    ~Channel() {}

private:
    int _wfd;          // 子进程管道读端文件描述符
    pid_t _pid;        // 子进程进程id
    std::string _name; // 子进程名称
};

void Execute(int tasknum, int processnum, std::vector<Channel> &processes)
{
    // 发送任务
    write(processes[processnum].GetWfd(), &tasknum, sizeof(int));
}

// 创建进程池
void CreatProcessPool(int num, std::vector<Channel> *processes, task_t task)
{
    // 给定子进程数量num，通过循环，先创建管道，再创建子进程，然后关闭各自不需要的端口
    for (int i = 0; i < num; i++)
    {

        int pipefd[2];
        pipe(pipefd);
        pid_t id = fork();
        if (id == 0)
        {
            close(pipefd[1]);

            // 将子进程读端重定向到标准输入，
            // 这样所有子进程都只在标准输入读取信息，可以完全忽略管道原先的读端文件描述符
            dup2(pipefd[0], 0);

            // 通过回调函数执行工作任务
            task();
            // 任务执行完毕退出程序，等待回收
            exit(0);
        }
        // 父进程关闭读端
        close(pipefd[0]);
        // 构造进程名字
        std::string name = "channel" + std::to_string(i);
        // 加入进程池,进行统一管理
        processes->push_back({pipefd[1], id, name});
    }
}

// 选择执行任务的进程
int SelectProcess(int maxsize)
{
    // 通过轮训的方式实现负载均衡
    static int size = 0;
    int num = size;
    size++;
    if (size == maxsize)
        size = 0;
    return num;
}
// 随机派发某一个任务
int SelectTaskNum()
{
    return rand() % TASKSIZE;
}
// 主控程序的单词任务执行
void CtrlProcessLoop(std::vector<Channel> &processes)
{
    // 选择任务
    int tasknum =SelectTaskNum();
    // 选择子进程
    int processnum = SelectProcess(processes.size());
    // 进程执行任务
    Execute(tasknum, processnum, processes);
    sleep(1);
}

// 执行任务的主控程序
void CtrlProcess(std::vector<Channel> &processes, int times = -1)
{
    // times为执行多少次任务
    if (times > 0)
    {
        while (times--)
        {
            CtrlProcessLoop(processes);
        }
    }
    else
    {
        // 默认一直执行
        while (true)
        {
            CtrlProcessLoop(processes);
        }
    }
}

// 销毁进程池
void DestroyProcesses(std::vector<Channel> &processes)
{
    // 先关闭文件描述符
    for (auto &channel : processes)
    {
        channel.CloseWfd();
    }
    // 在等待子进程退出
    for (auto &channel : processes)
    {
        channel.Wait();
    }
}

int main(int argc, char *argv[])
{

    std::vector<Channel> processes;

    // 需要指定进程池中进程的个数，命令行参数传入
    if (argc == 1)
    {
        std::cout << "Number of processes not specified!" << std::endl;
        exit(1);
    }
    int num = std::stoi(argv[1]);
    // 加载任务
    LoadTask();

    // 创建进程池
    CreatProcessPool(num, &processes, work);

    // 控制子进程
    CtrlProcess(processes, 10);

    // 回收进程池
    DestroyProcesses(processes);

    return 0;
}